package net.cocotea.sqltoy.lambda.factory.query;

import net.cocotea.sqltoy.lambda.common.SqlCharter;
import net.cocotea.sqltoy.lambda.factory.condition.AbstractLambdaCondition;
import net.cocotea.sqltoy.lambda.factory.condition.ICondition;
import net.cocotea.sqltoy.lambda.func.SFunction;
import net.cocotea.sqltoy.lambda.util.LambdaUtils;
import org.sagacity.sqltoy.model.EntityQuery;

import java.util.*;
import java.util.function.Consumer;

/**
 * Lambda实体查询类
 * 
 * @param <T> 实体类型
 * @author CoCoTea
 * @since 0.0.3
 */
public class LambdaEntityQuery<T> extends AbstractLambdaCondition<T> implements ILambdaEntityQuery<T> {

    private static final long serialVersionUID = -1605955950009577232L;

    private LambdaEntityQuery() {
        super();
    }

    private Class<T> entityClass = null;
    private StringJoiner selectSql = null;
    private StringJoiner unselectSql = null;
    private StringJoiner orderBySql = null;
    private StringJoiner orderByDescSql = null;
    private StringJoiner groupBySql = null;

    /**
     * sqltoy基础实体查询类
     *
     * @see EntityQuery
     */
    private EntityQuery entityQuery;

    public static <T> LambdaEntityQuery<T> create(Class<T> clz) {
        return new LambdaEntityQuery<T>().entityQuery(EntityQuery.create())
                .entityClass(clz);
    }

    protected LambdaEntityQuery<T> entityQuery(EntityQuery entityQuery) {
        this.entityQuery = entityQuery;
        return this;
    }

    protected LambdaEntityQuery<T> entityClass(Class<T> clz) {
        this.entityClass = clz;
        return this;
    }

    protected Class<T> entityClass() {
        return entityClass;
    }

    @SafeVarargs
    @Override
    public final LambdaEntityQuery<T> select(SFunction<T, Object>... cols) {
        if (selectSql == null) {
            selectSql = new StringJoiner(SqlCharter.COMMA);
        }
        selectSql.merge(LambdaUtils.getCols(cols));
        return this;
    }

    @SafeVarargs
    @Override
    public final ILambdaEntityQuery<T> unselect(SFunction<T, Object>... cols) {
        if (unselectSql == null) {
            unselectSql = new StringJoiner(SqlCharter.COMMA);
        }
        unselectSql.merge(LambdaUtils.getCols(cols));
        return this;
    }

    @Override
    public LambdaEntityQuery<T> eq(SFunction<T, ?> col, Object value) {
        super.eq(col, value);
        return this;
    }

    @Override
    public LambdaEntityQuery<T> neq(SFunction<T, ?> col, Object value) {
        super.neq(col, value);
        return this;
    }

    @Override
    public LambdaEntityQuery<T> gt(SFunction<T, ?> col, Object value) {
        super.gt(col, value);
        return this;
    }

    @Override
    public LambdaEntityQuery<T> gte(SFunction<T, ?> col, Object value) {
        super.gte(col, value);
        return this;
    }

    @Override
    public LambdaEntityQuery<T> lt(SFunction<T, ?> col, Object value) {
        super.lt(col, value);
        return this;
    }

    @Override
    public LambdaEntityQuery<T> lte(SFunction<T, ?> col, Object value) {
        super.lte(col, value);
        return this;
    }

    @Override
    public LambdaEntityQuery<T> like(SFunction<T, ?> col, String value) {
        super.like(col, value);
        return this;
    }

    @Override
    public LambdaEntityQuery<T> notLike(SFunction<T, ?> col, String value) {
        super.notLike(col, value);
        return this;
    }

    @Override
    public LambdaEntityQuery<T> leftLike(SFunction<T, ?> col, String value) {
        super.leftLike(col, value);
        return this;
    }

    @Override
    public LambdaEntityQuery<T> rightLike(SFunction<T, ?> col, String value) {
        super.rightLike(col, value);
        return this;
    }

    @Override
    public LambdaEntityQuery<T> in(SFunction<T, ?> col, Collection<?> values) {
        super.in(col, values);
        return this;
    }

    @Override
    public LambdaEntityQuery<T> notIn(SFunction<T, ?> col, Collection<?> values) {
        super.notIn(col, values);
        return this;
    }

    @Override
    public LambdaEntityQuery<T> between(SFunction<T, ?> col, Object value1, Object value2) {
        super.between(col, value1, value2);
        return this;
    }

    @Override
    public LambdaEntityQuery<T> where() {
        return this;
    }

    @Override
    public LambdaEntityQuery<T> and() {
        super.and();
        return this;
    }

    @Override
    public LambdaEntityQuery<T> nested(Consumer<ICondition<T>> action) {
        super.nested(action);
        return this;
    }

    @Override
    public LambdaEntityQuery<T> or() {
        super.or();
        return this;
    }

    @SafeVarargs
    @Override
    public final LambdaEntityQuery<T> orderBy(SFunction<T, Object>... cols) {
        if (orderBySql == null) {
            orderBySql = new StringJoiner(SqlCharter.COMMA);
        }
        orderBySql.merge(LambdaUtils.getCols(cols));
        return this;
    }

    @SafeVarargs
    @Override
    public final LambdaEntityQuery<T> orderByDesc(SFunction<T, Object>... cols) {
        if (orderByDescSql == null) {
            orderByDescSql = new StringJoiner(SqlCharter.COMMA);
        }
        orderByDescSql.merge(LambdaUtils.getCols(cols));
        return this;
    }

    @SafeVarargs
    @Override
    public final LambdaEntityQuery<T> groupBy(SFunction<T, Object>... cols) {
        if (groupBySql == null) {
            groupBySql = new StringJoiner(SqlCharter.COMMA);
        }
        groupBySql.merge(LambdaUtils.getCols(cols));
        return this;
    }

    @Override
    public EntityQuery toEntityQuery() {
        if (selectSql != null) {
            entityQuery.select(selectSql.toString());
        }
        if (unselectSql != null) {
            entityQuery.unselect(unselectSql.toString());
        }
        if (whereSql != null) {
            entityQuery.where(whereSql.toString());
        }
        if (orderBySql != null) {
            entityQuery.orderBy(orderBySql.toString());
        }
        if (orderByDescSql != null) {
            entityQuery.orderByDesc(orderByDescSql.toString());
        }
        if (namesAndValuesMap != null) {
            entityQuery.values(namesAndValuesMap);
        }
        if (groupBySql != null) {
            entityQuery.groupBy(groupBySql.toString());
        }
        return entityQuery;
    }

}
